package lib::Compare;

# This file is part of noplagia.
#
# noplagia is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# noplagia is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# noplagia; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
# Suite 330, Boston, MA  02111-1307 USA

use strict;
use Text::Compare;
use Data::Dumper;
use lib::TextTreatment;
use lib::Context;
require Exporter;
our ($VERSION,@ISA,@EXPORT,$context);

$VERSION = 0.01;

=head1 NAME

    lib::Compare - Comparaison de textes

=head1 SYNOPSIS

    use lib::Compare;
    my $pourcentage = compare($text1,$text2);

=head1 DESCRIPTION

    Ce module permet de comparer 2 textes.

=head1 FUNCTIONS

=over 4

=cut

@ISA    = qw(Exporter);
@EXPORT = qw(
    &compare &_compare
);

=item _compare

    my $pourcentage = compare($text1,$text2);
    Compare I<$text1> et I<$text2> and retourne un I<$pourcentage> de similitude.
    0 < I<$pourcentage> < 1

=cut

sub _compare($$) {
    my ($text1,$text2)  = @_;
    my $tc = new Text::Compare( memoize => 1, stip_html => 1 );
    return 0 unless ( $text1 && $text2);
    my $sim = $tc->similarity($text1, $text2);
    return $sim;
}


sub build_query{
    my $query = shift;
    my @query;
    $query = set_noPunctuation($query);
    @query = split(/[\p{P}\s\n]+/, $query);#!!spliter sur le set_noPunctuation !!
    @query = grep {length($_) >= 4;} @query;
    return @query;
}


# on cherche ou ca match de la maniere
# la plus simple
sub search_1{
    my ($text, @query) = @_;
    @query  =  map {quotemeta($_)} @query;
    my $expr = join ".{0,30}?", @query;
    if($text =~ m/($expr)/mgi){
        return ((pos($text) - length($1)),length($1));
    }
    return;
}


# retourne un booleen pour dire si
# il y a chevauchement
sub is_chevauche($$$){
    my ($debut, $fin, $pos) = @_;
    foreach (@$pos){
        if ($debut < $_->{fin} && $fin > $_->{debut}){
            return 1;
        }
    }
    return 0;
}


#truc de ouf
sub chevauche($$$$){
    my ($debut, $fin, $pos, $text_len) = @_;
    my @sorted_pos = sort {$a->{debut} > $b->{debut}} @$pos;
    my $last    = {debut => 0, fin=>0};
    my $current = {debut => 0, fin=>0};
    foreach(@sorted_pos){
        $current = $_;
        last if ($_->{fin} > $debut);
        $last = $_;
    }
    if ($current->{debut} == $last->{debut}) {
        return (
                debut => $current->{fin},
                fin   => $text_len,
            );
    }
    if ($current->{debut} < $fin){
        return ();
    }
    return (
            debut =>$last->{fin},
            fin   =>$current->{debut},
        );
}


sub set_pas_down($$$$$){
    my ($bornes_local, $bornes_web, $offset_local_down, $offset_web_down, $jump) = @_;
    my $jump_local;
    my $jump_web;
    if (%$bornes_local){
        if($offset_local_down + $jump > $bornes_local->{fin}){
            $jump_local = $bornes_local->{fin} - $offset_local_down;
        }else{
            $jump_local = $jump;
        }
        if($offset_web_down + $jump > $bornes_web->{fin}){
            $jump_web = $bornes_web->{fin} - $offset_web_down;
        }else{
            $jump_web = $jump;
        }
        if ($jump_local < $jump_web){
            return $jump_local;
        }else{
            return $jump_web;
        }
    }
}

sub set_pas_up($$$$$){
    my ($bornes_local, $bornes_web, $offset_local_up, $offset_web_up, $jump) = @_;
    my $jump_local;
    my $jump_web;
    if (%$bornes_local){
        if($offset_local_up - $jump < $bornes_local->{debut}){
            $jump_local = $offset_local_up - $bornes_local->{debut};
        }else{
            $jump_local = $jump;
        }
        if($offset_web_up - $jump < $bornes_web->{debut}){
            $jump_web = $offset_web_up - $bornes_web->{debut} ;
        }else{
            $jump_web = $jump;
        }
        if ($jump_local < $jump_web){
            return $jump_local;
        }else{
            return $jump_web;
        }
    }
}


sub elargissement($$$$$$$){
    my ($offset_web, $len_req_web, $offset_local, $len_req, $num_req, $text_web, $text, $pos) = @_;
    my $text_len     = length($text);
    my $text_web_len = length($text_web);

    #initialisation position de la requete
    my $offset_local_up   = $offset_local;
    my $offset_local_down = $offset_local + $len_req;

    #initialisation position de la requete
    my $offset_web_up   = $offset_web;
    my $offset_web_down = $offset_web + $len_req_web;

    #reglage des pas d'avancement et de comparaison
    my $jump = get_pref('jump');
    my $comp_simili = get_pref('compare_simili')/100;

    #initialisation des bornes locales et web
    my %bornes_local = &chevauche($offset_local_up, $offset_local_down, $pos, $text_len);
    my %bornes_web   = (debut => 0, fin => $text_web_len,);

    #vers le bas
    my $tmp_jump = &set_pas_down(\%bornes_local, \%bornes_web, $offset_local_down, $offset_web_down, $jump);

    my $string_local = substr($text, $offset_local_down, $tmp_jump);
    my $string_web   = substr($text_web, $offset_web_down, $tmp_jump);


    my $taux_similitude=0;
    my $somme_coeff=0;
    my $cmp = _compare($string_local, $string_web);
    while ( $cmp > $comp_simili){
        $offset_local_down += $tmp_jump;
        $offset_web_down   += $tmp_jump;
        $tmp_jump = &set_pas_down(\%bornes_local, \%bornes_web, $offset_local_down, $offset_web_down, $jump);
        $string_local = substr($text, $offset_local_down, $tmp_jump);
        $string_web   = substr($text_web, $offset_web_down, $tmp_jump);
        $taux_similitude += $cmp*$tmp_jump;
        $somme_coeff += $tmp_jump;
        $cmp = _compare($string_local, $string_web);
    }

    #vers le haut
    $tmp_jump = &set_pas_up(\%bornes_local, \%bornes_web, $offset_local_up, $offset_web_up, $jump);

    $string_local = substr($text, $offset_local_up - $tmp_jump , $tmp_jump);
    $string_web   = substr($text_web, $offset_web_up -$tmp_jump, $tmp_jump);

    $cmp = _compare($string_local, $string_web);
    while ( $cmp > $comp_simili ){
        $offset_local_up -= $tmp_jump;
        $offset_web_up   -= $tmp_jump;
        $tmp_jump = &set_pas_up(\%bornes_local, \%bornes_web, $offset_local_up, $offset_web_up, $jump);
        $string_local = substr($text, $offset_local_up - $tmp_jump, $tmp_jump);
        $string_web   = substr($text_web, $offset_web_up - $tmp_jump, $tmp_jump);
        $taux_similitude += $cmp*$tmp_jump;
        $somme_coeff += $tmp_jump;
        $cmp = _compare($string_local, $string_web);
    }
    if ($somme_coeff == 0){
        $taux_similitude = 0;
    }else{
        $taux_similitude = ($taux_similitude/$somme_coeff)*100;
        $taux_similitude = sprintf("%0.2f", $taux_similitude);
    }

    my $len  = $offset_local_down - $offset_local_up;
    my $lenw = $offset_web_down - $offset_web_up;

    return {
            num_req      => $num_req,
            offset_local => $offset_local_up,
            len          => $len,
            lenw         => $lenw,
            offset_web   => $offset_web_up,
            similitude   => $taux_similitude,
    };
}

sub parser{
    my($text,$pos,$web_context) = @_;
    my $text_len = length($text);
    my @parse;
    my $last_end = 0;
    foreach(@$pos){
        if ($last_end < $_->{debut}){
            my $taille = $_->{debut} - $last_end;
            my $content = substr($text, $last_end, $taille);
            $content =~ s/^(.{0,300}).*?(.{0,300})$/\1 \[...\] \2/ if $web_context and $taille > 600;
            push @parse, {
                    content => $content,
                    len     => $_->{debut} - $last_end,
                };
        }
        push @parse, {
                content     => substr($text, $_->{debut}, $_->{len}),
                similitude  => $_->{similitude},
                num_req     => $_->{num_req},
                len         => $_->{len},
                plagia      => 1,
                pourcentage => sprintf("%0.2f", $_->{len}/$text_len*100),
            };
        $last_end = $_->{debut} + $_->{len};
    }
    if ($last_end < $text_len){
        my $taille = $text_len - $last_end;
        my $content = substr($text, $last_end, $taille);
        $content =~ s/^(.{0,300}).*?(.{0,300})$/\1 \[...\] \2/m if $web_context and $taille > 600;
        push @parse, {
                content => $content,
                len     => $text_len - $last_end,
            };
    }
    return \@parse;
}

sub compare($$){
    my($result, $text) = @_;
    my $comp_simili = get_pref('compare_simili');
    my @plagia_local;
    my %plagia_web;
    my $position = 0;
    my @pos;
    my @Result;
    foreach my $r (@$result){
        my $urls = $r->{urls};
        foreach my $url (@$urls){
            if (exists $url->{content}) {
                my $text_offset = $url->{text_offset};
                my $url_name    = $url->{url};
                my $text_web    = $url->{content};
                foreach (@$text_offset){
                    my $req     = $_->{text};
                    my $num_req = $_->{offset};
                    my $len_req = length($req);
                    if ($_->{match} > $comp_simili){
                        next if ( &is_chevauche($_->{position}, $_->{position}+$len_req, \@pos) );
                        #trouver ou on match sur le site distant
                        my @query = &build_query($req);
                        my ($offset_web, $len_req_web) = &search_1($text_web, @query);
                        unless (defined($offset_web) && defined($len_req_web)){
                            warn "requete non trouvee sur le site distant --> Ameliorer la fonction de match sur site
                            distant -- non critique";
                            warn join " ", @query;
                            next;
                        }
                        my ($offset_local_query, $len_query) = &search_1($text, @query);
                        unless (defined($offset_local_query) && defined($len_query)){
                            warn "requete non trouvee sur le site LOCAL -- erreur critique";
                            #mieux que rien :
                            $offset_local_query = $_->{position};
                        }
                        my $h =  &elargissement($offset_web, $len_req_web, $offset_local_query, $len_query, $num_req,$text_web, $text, \@pos);
                        push @{$plagia_web{$url->{url}}}, {
                                    debut      => $h->{offset_web},
                                    fin        => $h->{offset_web} + $h->{lenw},
                                    num_req    => $h->{num_req},
                                    len        => $h->{lenw},
                                };
                        push @pos, {
                                debut => $h->{offset_local},
                                fin   => $h->{offset_local} + $h->{len},
                                similitude => $h->{similitude},
                                num_req    => $h->{num_req},
                                len        => $h->{len},
                            };
                    }
                }
            }
            if (exists $plagia_web{$url->{url}}){
                $url->{content} = &parser($url->{content}, $plagia_web{$url->{url}},1);
                $r->{plein} = 1;
            }else{
                undef($url);
            }
        }
        push @Result ,$r if $r->{plein};
    }
    
    @pos = sort {$a->{debut} > $b->{debut}} @pos;
    my $parse = &parser($text,\@pos);
    return (\@Result, $parse);
}


END { }
1;
__END__

=back

=head1 AUTEUR

    noplagia dev team

    http://noplagia.org

=cut
